﻿using System;
using System.Collections.Generic;
using System.Linq;
using PSE.Updater.Common.Providers.Interfaces;
using PSE.Updater.Common.Gateway.DataContract;
using PSE.Updater.Common;
using PSE.Updater.Common.Providers;
using PSE.Framework.ErrorLogging;

namespace PSE.Updater.Server.Engine.Providers.ConfigurationFile
{
    /// <summary>
    /// Classe responsável por gerenciar a disponibilização de atualizações
    /// </summary>
    internal sealed class UpdateDiscovery : IUpdateDiscovery
    {
        private CacheManager _cacheManager;
        #region [ Singleton ]
        private static object _singletonLock = new object();
        private static IUpdateDiscovery _updateDiscovery;
        public static IUpdateDiscovery Instance
        {
            get
            {
                lock (_singletonLock)
                {
                    if (_updateDiscovery == null)
                        _updateDiscovery = new UpdateDiscovery();

                    return _updateDiscovery;
                }

            }
        }
        #endregion [ Singleton ]

        /// <summary>
        /// Construtor
        /// </summary>
        private UpdateDiscovery()
        {
            _cacheManager = CacheManager.Instance;
        }

        /// <summary>
        /// Obtém todos os arquivos disponíveis para a avaliação de Atualização
        /// </summary>
        /// <returns>Todos os arquivos disponíveis para a avaliação de Atualização</returns>
        public IList<IFileDetails> GetAllFiles(string productName)
        {
            return _cacheManager.GetCopyList(productName);
        }

        /// <summary>
        /// Obtém as Atualizações disponíveis
        /// </summary>
        /// <param name="productName">Nome do Produto</param>
        /// <param name="moduleName">Nome do Módulo</param>
        /// <param name="version">Versão do Módulo</param>
        /// <returns>Atualizações disponíveis</returns>
        public IList<IFileDetails> GetAvailableUpdates(ProviderType type, List<ModuleVersion> moduleVersions)
        {

            if (moduleVersions == null || moduleVersions.Count == 0)
            {
                ErrorLogger.WriteLog(LogType.Error, "Lista de módulos não informada ou vazia");
                throw new ArgumentNullException("moduleVersions");
            }

            /*Updateds Client Files*/
            List<IFileDetails> updateFiles = new List<IFileDetails>();
            foreach (var module in moduleVersions)
            {
                IList<IFileDetails> cachedFiles = _cacheManager.GetCopyList(module.SystemName, module.ModuleName);
                if (cachedFiles != null && cachedFiles.Count > 0)
                {

                    var files = (from file in cachedFiles
                                 where CompareVersion(file.Version, module.Version)
                                 select file).ToList();
                    if (files != null && files.Count > 0)
                    {
                        SetFileStatus(files, PackageStatus.Valid);
                        updateFiles.AddRange(files);
                    }
                }
            }

            var products = from module in moduleVersions
                           group module by module.SystemName into product
                           select new { Name = product.Key };

            /*Missing Client Files*/
            /*
            foreach (var product in products)
            {
                IList<IFileDetails> cachedFiles = _cacheManager.GetCopyList(product.Name);

                cachedFiles = cachedFiles.Where(c => c.SystemName.Equals(product.Name, StringComparison.InvariantCultureIgnoreCase)).ToList();
                var files = (from file in cachedFiles
                             where !moduleVersions.Exists(m => m.ModuleName.Equals(file.ModuleName, StringComparison.InvariantCultureIgnoreCase))
                             select file).ToList();
                if (files != null && files.Count > 0)
                {
                    SetFileStatus(files, PackageStatus.New);
                    updateFiles.AddRange(files);
                }
            }
            */

            return updateFiles;
        }

        private void SetFileStatus(List<IFileDetails> files, PackageStatus status)
        {
            foreach (var file in files)
                file.Status = status;
        }

        /// <summary>
        /// Compara duas versões do arquivo.
        /// Nesse caso, a versão é a Data/Hora da última alteração do arquivo
        /// </summary>
        /// <param name="version1">Versão a ser comparada</param>
        /// <param name="version2">Versão a ser comparada</param>
        /// <returns>Caso a versão1 (Data/Hora da última alteração do arquivo) for diferente da versão2, 
        /// o sistema assume que esse arquivo precisa estar disponível para download
        /// </returns>
        private bool CompareVersion(string version1, string version2)
        {
            DateTime date1;
            if(!DateTime.TryParse(version1, out date1))
            {
                throw new Exception("");
            }
            DateTime date2;
            if (!DateTime.TryParse(version2, out date2))
            {
                throw new Exception("");
            }

            return (date1 != date2);
        }
    }
}